<?php

namespace Service\CommodityService;

use Service\BaseService;
use App\Amqp\Producer\CommodityStockMessageProducer;
use Hyperf\Amqp\Producer;
use Hyperf\Utils\ApplicationContext;
use Service\Code\Normal;
use Service\AppService\IdService;
use Service\AppService\RedisRedLock;
use App\Model\CommodityModel\CommodityArchives;
use Hyperf\DbConnection\Db;
use App\Model\CommodityModel\Stockinfo;
use Service\AppService\SystemCacheConfigService;
use App\Model\CommodityModel\StockInfoUser;
use App\Model\CommodityModel\StockInfoUserLog;
use Throwable;

class CommodityStockService extends BaseService
{
    const STOCK_NUM_ADD = '+';
    const STOCK_NUM_SUB = '-';
    
    /**
     * 库存扣除接口、加库存
     * @param string $BuID
     * @param array $stockDeductInfo
     * @param string $symbol
     */
    public function orderReduceStock( int $BuID, array $stockDeductInfo = [] ,string $symbol)
    {
        $stockList = [];
        foreach ($stockDeductInfo as $item ){
            $stockList[] = [
                'event_id'  => $item['stockInfoID']
            ];
            unset($item);
        }
        
        //是否开启了锁仓功能
        $sysconfig = SystemCacheConfigService::instance()->getCacheInfoBySystemCode('sysconfig');

        //是否锁库功能
        $IsOpenLockStock = $sysconfig['IsOpenLockStock'] ?? false;
        
        try {
    
            $kVal = IdService::instance()->getOtherID();
            $lock = RedisRedLock::instance()->getMultiLock($stockList,$kVal);
            if( !$lock ) {
                return Normal::returnCode(Normal::FAIL,"当前商品抢购中，请稍后再试！");
            }
    
            //开始执行扣除库存
            CommodityArchives::instance()->startTrans();
            
            //开始读取一次where in lock
            /* $stockIDs = array_column($stockList, 'event_id');
            $this->queryStockLockListByStockIDs($stockIDs); */
            
            //需要扣除的目标库存
            foreach ($stockDeductInfo as $item)
            {
                
                //如果当前用户对应批次是锁仓数据，则进行扣减及恢复锁仓数据
                $stock_id   = (string)$item['stockInfoID'];
                $goods_name = $item['goodsName'];
                $number     = (int)$item['number'];
                $flag       = false;
                
                //如果有开启锁仓功能的云仓
                if( $IsOpenLockStock )  
                {
                    $stockUserInfo = StockInfoUser::instance()->getStockInfoUserByBidAndStockID($BuID, $stock_id);
                    
                    //如果有启用的锁仓数据
                    if( !empty($stockUserInfo) ) 
                    {
                        //判断库存是否足够
                        if( self::STOCK_NUM_SUB == $symbol && $stockUserInfo['StockRemainNumber'] < $number ) 
                        {
                            CommodityArchives::instance()->rollback();
                            return Normal::returnCode(Normal::FAIL,"当前商品【{$goods_name}】锁仓库存不足[100000]！");
                        }
                        
                        //变化锁仓库存
                        $updateLockUserStock = [
                            'StockRemainNumber' => Db::raw("StockRemainNumber {$symbol} {$number}"),
                            'UpdateTime'        => date("Y-m-d H:i:s")
                        ];
                        
                        //扣减库存
                        if( self::STOCK_NUM_SUB == $symbol ) {
                            $res = StockInfoUser::instance()->where('ID',$stockUserInfo['ID'])->where('StockRemainNumber','>=',$number)->where('StockRemainNumber','>',0)->update($updateLockUserStock);
                        } else {
                            $res = StockInfoUser::instance()->where('ID',$stockUserInfo['ID'])->update($updateLockUserStock);
                        }
                        
                        if( !$res ) {
                            CommodityArchives::instance()->rollback();
                            return Normal::returnCode(Normal::FAIL,"当前商品【{$goods_name}】锁仓库存不足[100001]！");
                        }
                        
                        $flag = true;
                    }
                }

                //更新原始库存表操作
                $_update_field = $flag ? 'NumberLock' : 'Number';
                $_update_stock = [
                    $_update_field => Db::raw("{$_update_field} {$symbol} {$number}"),
                    'UpdateTime' => date("Y-m-d H:i:s")
                ];
                
                //写入锁仓库存变化日志
                $type = $symbol == self::STOCK_NUM_ADD ? StockInfoUserLog::RECOVER : StockInfoUserLog::REDUCE;
                //查询一次原始库存
                $originalStockInfo = Stockinfo::instance()->where('ID',$stock_id)->first();
                if (empty($originalStockInfo)){
                    CommodityArchives::instance()->rollback();
                    return Normal::returnCode(Normal::FAIL,"当前商品【{$goods_name}】锁仓库存不足[100004]！");
                }
                $originalStockInfo=$originalStockInfo->toArray();
                $originalNumber = bcadd($originalStockInfo['Number'],$originalStockInfo['NumberLock']);
                $result = StockInfoUserLog::instance()->addUserConsumeLog($BuID, $stock_id, $number,$originalNumber, $type);
                if( ! $result ) {
                    CommodityArchives::instance()->rollback();
                    return Normal::returnCode(Normal::FAIL,"当前商品【{$goods_name}】锁仓库存不足[100002]！");
                }
                
                //扣除原始库存锁仓总库存
                if( self::STOCK_NUM_SUB == $symbol ) {
                    $res = Stockinfo::instance()->where('ID',$stock_id)->where($_update_field,'>=',$number)->where($_update_field,'>',0)->update($_update_stock);
                } else {
                    $res = Stockinfo::instance()->where('ID',$stock_id)->update($_update_stock);
                }
                
                if( !$res) {
                    CommodityArchives::instance()->rollback();
                    return Normal::returnCode(Normal::FAIL,"当前商品【{$goods_name}】锁仓库存不足[100003]！");
                }
            }
            
            //库存扣除成功，通知MQ
            $SkuIDs = array_column($stockDeductInfo, 'SkuID');
            $SkuIDs = array_map(function( & $value ){
                return (string)$value;
            },$SkuIDs);
            $res = $this->publish($SkuIDs);
            if( !$res['code'] != Normal::SUC ) {
                CommodityArchives::instance()->rollback();
                return Normal::returnCode(Normal::SUC,"当前商品抢购中，请稍后再试！");
            }
    
            CommodityArchives::instance()->commit();
            RedisRedLock::instance()->getMultiUnLock($stockList, $kVal);
            return Normal::returnCode(Normal::SUC,"库存扣除操作成功");
        } catch ( \Throwable $t ) {
            CommodityArchives::instance()->rollback();
            RedisRedLock::instance()->getMultiUnLock($stockList, $kVal);
            return Normal::returnCode(Normal::FAIL,"暂无库存[{$t->getMessage()}]");
        }
    }
    
    /**
     * 查询库存范围锁
     * @param array $stockIDs
     */
    public function queryStockLockListByStockIDs( array $stockIDs )
    {
        return Stockinfo::instance()->whereIn('ID',$stockIDs)->lockForUpdate()->get()->toArray();
    }
    
    /**
     * 消息发布到MQ
     * {
     *   "SkuIDList":[
     *     "398844155360446338",
         "401817302418327426",
         "401817302430910338"
     *   ]
     * }
     * @param array $body
     */
    public function publish( $body = [] )
    {
        $contents = [
            "MessageType"   => 'CommodityArchiveChange',
            "DataList"      => [
                'SkuIDList' => $body
            ]
        ];
        $publishMsg = json_encode($contents,JSON_UNESCAPED_UNICODE);
        try {
            $message = new CommodityStockMessageProducer($contents);
            $producer = ApplicationContext::getContainer()->get(Producer::class);
            $producer->produce($message);
            logger('info','CommodityStockMessageProducer')->info($publishMsg);
            return Normal::returnCode(Normal::SUC,"发布成功");
        } catch ( Throwable $e ) {
            logger('info','CommodityStockMessageProducer-error')->info($e->getMessage());
            return Normal::returnCode(Normal::FAIL,$e->getMessage());
        }
    }
}